<?php
require("../../IncludeManager.php");

using("Utils_Queue");
using("Expression_Operator");
using("Expression_Operator_Factory");
using("Expression_Operand_Table");
using("Expression_Operand");
using("Expression_Operand_Variable");

class Property {
	
	public $_name;
	
	public $_column;
	
	public $_value;
	
	public function __construct($name, $column, $value) {
		$this->_name = $name;
		$this->_column = $column;
		$this->_value = $value;
	}
}

class Table {
	
	public $_name;
	
	public $_table;
	
	public function __construct($name, $table) {
		$this->_name = $name;
		$this->_table = $table;
	}
}

/**
 * Construct a update query
 * 
 * <p>You can get a sense for how this would work for other queries by looking
 * at what follows.  Some serious questions not yet resolved are how to apply
 * this approach to cover more than one table.  I am not even positive you can
 * do an update across multiple tables in a single query, which would at least
 * alleviate the problem for this case.  That said there would still need to be
 * a way to solve the riddle for select statements.
 * 
 * TODO confirm that inserts, updates, and deletes can only involve one table
 * per a query.
 *
 */
class Expression_Builder_Update {
	
	/**
	 * @var Expression_Operator
	 */
	private $_set;
	
	/**
	 * @var Expression_Operator
	 */
	private $_update;
	
	/**
	 * Queue for expressions
	 *
	 * @var Utils_Queue
	 */
	private $_queue;
	
	public function __construct(Table $table) {
		# construct queue and factory
		$this->_queue = new Utils_Queue();
		$factory = Expression_Operator_Factory::getSingleton();
		# create a set and update operator and a table operand
		$this->_set = $factory->construct("set", array());
		$operand = new Expression_Operand_Table($table->_table);
		$this->_update = $factory->construct("update", array($operand, $this->_set));
	}
	
	public function bindKey(Property $bound) {
		$factory = Expression_Operator_Factory::getSingleton();
		# make operands for the property column and value
		$column = new Expression_Operand($bound->_column);
		$value = new Expression_Operand_Variable($bound->_value);
		$equals = $factory->construct("equals", array($column, $value));
		# add them to a where clause and add that to the update clause
		$where = $factory->construct("where", array($equals));
		$this->_update->addOperand($where);
	}
	
	public function bindProperty(Property $bound) {
		$factory = Expression_Operator_Factory::getSingleton();
		# create operands for the property column and value
		$column = new Expression_Operand($bound->_column);
		$value = new Expression_Operand_Variable($bound->_value);
		# set the two as being equal and then store the new operator in the queue
		$equals = $factory->construct("equals", array($column, $value));
		$this->_queue->add($equals);
	}
	
	public function __toString() {
		$factory = Expression_Operator_Factory::getSingleton();
		# go through the queue until there is only one operand
		# take two off each time and AND them together putting the AND on the queue
		while ($this->_queue->size() > 1) {
			$and = $factory->construct("and", array($this->_queue->remove(), $this->_queue->remove()));
			$this->_queue->add($and);
		}
		# add the resulting AND operator to the SET operator
		$this->_set->addOperand($this->_queue->remove());
		return $this->_update->evaluate();
	}
}

$update = new Expression_Builder_Update(new Table("foo", "users"));
$update->bindKey(new Property("id", "id", "7"));
$update->bindProperty(new Property("username", "tag", "johndoe"));
$update->bindProperty(new Property("password", "hashed_password", "apsw4john"));
$update->bindProperty(new Property("date_modified", "date_modified", time() . ""));
$update->bindProperty(new Property("state", "state", "0"));
echo $update;
?>